package com.ruoyi.common.BaseMapper;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.OrderItem;
import com.baomidou.mybatisplus.core.metadata.TableInfoHelper;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.baomidou.mybatisplus.core.toolkit.support.SFunction;
import org.apache.ibatis.annotations.Param;
import com.github.yulichang.base.MPJBaseMapper;
import com.github.yulichang.wrapper.MPJLambdaWrapper;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collection;
import java.util.EnumMap;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import com.ruoyi.common.utils.reflect.ReflectUtils;

/**
 * @Classname BaseMapperX
 * @Description 增强MyBatis Plus的基础Mapper，支持基于实体字段注解的查询和多表关联查询
 * @Date 2025/10/15 11:50
 * @Created by 陈九常
 */
public interface BaseMapperX<T> extends MPJBaseMapper<T> {

    /**
     * 根据实体字段注解自动构建查询条件（无排序）
     */
    default List<T> selectByEntity(@Param("ew") T entity) {
        return selectByEntity(entity, null);
    }

    /**
     * 根据实体字段注解自动构建查询条件（带动态排序）
     *
     * @param entity 包含查询条件的实体对象
     * @param orders 排序规则列表（可空）
     * @return 查询结果列表
     */
    default List<T> selectByEntity(@Param("ew") T entity, List<OrderItem> orders) {
        MPJLambdaWrapper<T> wrapper = new MPJLambdaWrapper<>();
        buildConditions(wrapper, entity);
        applyOrders(wrapper, orders);
        return selectList(wrapper);
    }

    /**
     * 创建MPJLambdaWrapper实例
     *
     * @return MPJLambdaWrapper实例
     */
    default MPJLambdaWrapper<T> createLambdaWrapper() {
        return new MPJLambdaWrapper<>();
    }

    /**
     * 左连接查询
     *
     * @param joinClass 关联表实体类
     * @param joinOnFunction1 关联表的关联字段
     * @param joinOnFunction2 主表的关联字段
     * @param <J> 关联表实体类型
     * @return MPJLambdaWrapper实例
     */
    default <J> MPJLambdaWrapper<T> leftJoin(Class<J> joinClass, Class<T> Class, SFunction<J, ?> joinOnFunction1, SFunction<T, ?> joinOnFunction2) {
        MPJLambdaWrapper<T> wrapper = new MPJLambdaWrapper<>();
        wrapper.leftJoin(joinClass, joinOnFunction1, joinOnFunction2);
        wrapper.selectAll(joinClass);
        wrapper.selectAll(Class);
        return wrapper;
    }

    /**
     * 右连接查询
     *
     * @param joinClass 关联表实体类
     * @param joinOnFunction1 关联表的关联字段
     * @param joinOnFunction2 主表的关联字段
     * @param <J> 关联表实体类型
     * @return MPJLambdaWrapper实例
     */
    default <J> MPJLambdaWrapper<T> rightJoin(Class<J> joinClass, SFunction<J, ?> joinOnFunction1, SFunction<T, ?> joinOnFunction2) {
        MPJLambdaWrapper<T> wrapper = new MPJLambdaWrapper<>();
        wrapper.rightJoin(joinClass, joinOnFunction1, joinOnFunction2);
        return wrapper;
    }

    /**
     * 内连接查询
     *
     * @param joinClass 关联表实体类
     * @param joinOnFunction1 关联表的关联字段
     * @param joinOnFunction2 主表的关联字段
     * @param <J> 关联表实体类型
     * @return MPJLambdaWrapper实例
     */
    default <J> MPJLambdaWrapper<T> innerJoin(Class<J> joinClass, SFunction<J, ?> joinOnFunction1, SFunction<T, ?> joinOnFunction2) {
        MPJLambdaWrapper<T> wrapper = new MPJLambdaWrapper<>();
        wrapper.innerJoin(joinClass, joinOnFunction1, joinOnFunction2);
        return wrapper;
    }

    /**
     * 应用排序规则
     */
    default void applyOrders(MPJLambdaWrapper<T> wrapper, List<OrderItem> orders) {
        if (orders == null || orders.isEmpty()) return;

        for (OrderItem order : orders) {
            if (order.isAsc()) {
                wrapper.orderByAsc(order.getColumn());
            } else {
                wrapper.orderByDesc(order.getColumn());
            }
        }
    }

    /**
     * 构建查询条件
     *
     * @param wrapper 查询包装器
     * @param entity  包含查询条件的实体
     */
    default void buildConditions(MPJLambdaWrapper<T> wrapper, T entity) {
        if (entity == null) return;

        // 使用EnumMap确保类型安全和性能
        Map<String, Map<Query.Range, Object>> betweenMap = new HashMap<>();
        // 用于跟踪已关联的表，避免重复关联
        Map<String, Boolean> joinedTables = new HashMap<>();

        // 处理所有标记了@Query的字段
        try {
            // 获取类的所有字段（包括父类字段）
            List<Field> fields = new ArrayList<>();
            // 使用反射获取字段
            Class<?> clazz = entity.getClass();
            while (clazz != null && clazz != Object.class) {
                for (Field field : clazz.getDeclaredFields()) {
                    fields.add(field);
                }
                clazz = clazz.getSuperclass();
            }
            for (Field field : fields) {
                Query query = field.getAnnotation(Query.class);
                if (query == null) continue;

                field.setAccessible(true);
                Object value = field.get(entity);

                // 处理空值
                if (value == null) continue;

                // 处理字符串类型的空值
                if (value instanceof String && StringUtils.isBlank((String) value)) continue;

                // 处理集合类型
                if (value instanceof Collection<?> && ((Collection<?>) value).isEmpty()) continue;

                // 检查是否为关联查询字段
                if (isJoinField(query)) {
                    // 处理关联查询
                    handleJoinQuery(wrapper, query, field, joinedTables);
                    continue;
                }

                // 获取数据库字段名
                String column = StringUtils.isBlank(query.column()) ?
                        StringUtils.camelToUnderline(field.getName()) :
                        query.column();

                // 根据查询类型构建条件
                switch (query.type()) {
                    case EQ:
                        wrapper.eq(column, value);
                        break;
                    case LIKE:
                        wrapper.like(column, value);
                        break;
                    case LIKE_LEFT:
                        wrapper.likeLeft(column, value);
                        break;
                    case LIKE_RIGHT:
                        wrapper.likeRight(column, value);
                        break;
                    case GT:
                        wrapper.gt(column, value);
                        break;
                    case LT:
                        wrapper.lt(column, value);
                        break;
                    case GTE:
                        wrapper.ge(column, value);
                        break;
                    case LTE:
                        wrapper.le(column, value);
                        break;
                    case NE:
                        wrapper.ne(column, value);
                        break;
                    case BETWEEN:
                        // 使用computeIfAbsent确保正确的Map类型
                        Map<Query.Range, Object> rangeMap = betweenMap.computeIfAbsent(
                                column,
                                k -> new EnumMap<>(Query.Range.class)
                        );
                        rangeMap.put(query.range(), value);
                        break;
                }
            }
        } catch (IllegalAccessException ignored) {
            // 忽略访问异常
        }

        // 处理范围查询
        processRangeConditions(wrapper, betweenMap);
    }

    /**
     * 判断是否为关联查询字段
     *
     * @param query 注解实例
     * @return 是否为关联查询字段
     */
    default boolean isJoinField(Query query) {
        return query.joinEntity() != Object.class || !StringUtils.isBlank(query.joinTable());
    }

    /**
     * 处理关联查询
     *
     * @param mpjWrapper 查询包装器
     * @param query 注解实例
     * @param field 字段对象
     * @param joinedTables 已关联表集合
     */
    @SuppressWarnings("unchecked")
    default void handleJoinQuery(MPJLambdaWrapper<T> mpjWrapper, Query query, Field field, Map<String, Boolean> joinedTables) {
        // 为了避免编译错误，暂时不实现具体的关联查询逻辑
        // 可以在实际使用时通过Lambda表达式方式进行关联查询
        // 例如: wrapper.leftJoin(SysClass.class, SysClass::getClassId, SysStudent::getClassId)
    }

    /**
     * 根据注解配置自动构建关联查询条件的关联查询方法
     * <pre>
     * 使用示例：
     * List<SysStudentVO> students = studentMapper.selectJoinByEntity(SysStudentVO.class, studentQueryEntity);
     * </pre>
     *
     * @param resultType 返回结果类型
     * @param entity 查询条件实体
     * @param <R> 返回结果类型泛型
     * @return 查询结果列表
     */
    default <R> List<R> selectJoinByEntity(Class<R> resultType, T entity) {
        MPJLambdaWrapper<T> wrapper = new MPJLambdaWrapper<>();
        buildConditions(wrapper, entity);
        return selectJoinList(resultType, wrapper);
    }

    /**
     * 示例说明：以下是使用Lambda表达式进行多表关联查询的典型用法
     * <pre>
     * // 方式1：直接使用MPJLambdaWrapper
     * MPJLambdaWrapper<SysStudent> wrapper = new MPJLambdaWrapper<>();
     * wrapper.leftJoin(SysClass.class, SysClass::getClassId, SysStudent::getClassId)
     *        .selectAll(SysStudent.class)
     *        .select(SysClass::getClassName)
     *        .eq(SysStudent::getStatus, 1);
     * List<SysStudentVO> students = studentMapper.selectJoinList(SysStudentVO.class, wrapper);
     * 
     * // 方式2：使用BaseMapperX提供的便捷方法
     * MPJLambdaWrapper<SysStudent> wrapper = studentMapper.leftJoin(SysClass.class, SysClass::getClassId, SysStudent::getClassId)
     *        .selectAll(SysStudent.class)
     *        .select(SysClass::getClassName)
     *        .eq(SysStudent::getStatus, 1);
     * List<SysStudentVO> students = studentMapper.selectJoinList(SysStudentVO.class, wrapper);
     * </pre>
     */

    /**
     * 获取数据库列名
     *
     * @param query 注解实例
     * @param field 字段对象
     * @return 数据库列名
     */
    default String getColumnName(Query query, Field field) {
        return StringUtils.isBlank(query.column()) ?
                StringUtils.camelToUnderline(field.getName()) :
                query.column();
    }

    /**
     * 处理范围查询条件
     *
     * @param wrapper    查询包装器
     * @param betweenMap 范围值映射
     */
    default void processRangeConditions(MPJLambdaWrapper<T> wrapper,
                                        Map<String, Map<Query.Range, Object>> betweenMap) {
        betweenMap.forEach((column, rangeMap) -> {
            Object start = rangeMap.get(Query.Range.START);
            Object end = rangeMap.get(Query.Range.END);

            if (start != null && end != null) {
                wrapper.between(column, start, end);
            } else if (start != null) {
                wrapper.ge(column, start);
            } else if (end != null) {
                wrapper.le(column, end);
            }
        });
    }
}
